/*
 * IMPORTANT NOTE:
 * It's inadvisable to run a coprocessor in the same thread as the
 * control processor, because of the recursive mutexes we use.
 * This means that we can't rely on the locking mechanisms that protect
 * variables, etc.
 */
 
#ifndef __SC_H_INCLUDED
#define __SC_H_INCLUDED

#include <scos.h>

#ifdef __cplusplus
extern "C" {
#endif

/* *** TO BE REMOVED *** */
#define SC_MAX_BUFFERS    2048
#define SC_MAX_OPERATIONS 2048
#define SC_MAX_ARGUMENTS  8

/* For instrumented traces */
#define IF     0
#define SWITCH 1
#define LOOP   2

#ifdef __cplusplus
class STREAM
{
};
#else
typedef int STREAM;
#endif

void scInit(int argc, char **argv);
void scMapNextProcessor(void);

#ifdef __MACRO_VARARGS_SUPPORTED__
#define scOp(format, op, ...) _scOp(__LINE__, 0, format, op, __VA_ARGS__)
#endif

#define scOp0(format, op) _scOp(__LINE__, 0, format, op)
#define scOp1(format, op, a) _scOp(__LINE__, 0, format, op, a)
#define scOp2(format, op, a, b) _scOp(__LINE__, 0, format, op, a, b)
#define scOp3(format, op, a, b, c) _scOp(__LINE__, 0, format, op, a, b, c)
#define scOp4(format, op, a, b, c, d) _scOp(__LINE__, 0, format, op, a, b, c, d)
#define scOp5(format, op, a, b, c, d, e) _scOp(__LINE__, 0, format, op, a, b, c, d, e)
#define scOp6(format, op, a, b, c, d, e, f) _scOp(__LINE__, 0, format, op, a, b, c, d, e, f)
#define scOp7(format, op, a, b, c, d, e, f, g) _scOp(__LINE__, 0, format, op, a, b, c, d, e, f, g)

void * _scOp(int line, int parent, char *format, int op, ...);
#ifdef __MACRO_VARARGS_SUPPORTED__
#define scThread(format, op, ...) _scThread(#op, format, op, __VA_ARGS__)
#endif

#define scThread0(format, op) _scThread(#op, format, op)
#define scThread1(format, op, a) _scThread(#op, format, op, a)
#define scThread2(format, op, a, b) _scThread(#op, format, op, a, b)
#define scThread3(format, op, a, b, c) _scThread(#op, format, op, a, b, c)
#define scThread4(format, op, a, b, c, d) _scThread(#op, format, op, a, b, c, d)

#ifdef __cplusplus
void _scThread(char *format, char *text, void (*op)(...), ...);
#else
void _scThread(char *format, char *text, void (*op)(), ...);
#endif
void _finalize(void *address);
void _block(void *address);
void _schedule(int make_it_run);
void _cycle(void *address);
void _multi(void *address);
void _allmulti(int multiuse);
void _mapto(int _affinity);
void _awaitall(void);
void _setbuffersize(int size);
int scExit(void);

int scTrace(int type, int line, int parent, int condition);

int    *scint(int i);
double *scdbl(double f);

#define READ 0
#define read(a, b) scOp2("dI", READ, a, b)

#define WRITE 1
#define write(a, b) scOp2("id", WRITE, a, b)

#define GENERATE 2
#define generate(a, b) scOp2("dI", GENERATE, a, b)

#define DISCARD 3
#define discard(a) scOp1("i", DISCARD, a)

#define DISPLAY 4
#define display(a) scOp1("i", DISPLAY, a)

#define CAPTURE 5
#define capture(a) scOp1("I", CAPTURE, a)

#define TRANSPORT 6
#define LOAD      7
#define CYCLE     8

#define GENSIZE   9

#define SPLIT 10
#define split(a, b, c) scOp3("iII", SPLIT, a, b, c)

// These definitions are necessary for the Future template class
typedef struct sc_instance_s
{
  int id, virtualproducer, blocked, finalized, temporary, multiuse;

  struct sc_var_s *variable;
  struct sc_thread_s *thread;

  void *address;
} sc_instance_t;

sc_instance_t *scManFindInstance(void *address);
sc_instance_t *scManAddInstance(struct sc_var_s *variable, void *address);

#ifdef __cplusplus
}
#endif

#ifdef __cplusplus
template <class T>
class Future
{
public:
  Future() { }

  ~Future() {
    _finalize(&data);
  }

  // Assign from primitive
  T operator=(const T &rhs) {
    return data = rhs;
  }

  // Assign from future
  Future<T> operator=(const Future<T> &rhs) {
    sc_instance_t *i = scManFindInstance((void*)&rhs.data);
    scManAddInstance(i->variable, &data)->thread = i->thread;
    return *this;
  }

  // Pass to remote function
  void *operator&() {
    return &data;
  }

  // Touch
  operator T() {
    _block(&data);
    return data;
  }

private:
  Future(const Future<T> &rhs) { }

  T data;
};

#endif

#endif /* __SC_H_INCLUDED */
